package com.shawn.springboot.integeration.common.config.shiro.filter;

import com.shawn.springboot.integeration.common.config.shiro.token.JWTToken;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

/**
 * @author shawn
 * @description: JWT 过滤器
 * @date 2023/12/17
 */
public class JWTFilter extends BasicHttpAuthenticationFilter {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 如果带有 token，则对 token 进行检查，否则直接通过
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest req, ServletResponse res, Object mappedValue)
            throws UnauthorizedException {
        // 判断请求是否携带 token
        HttpServletRequest HttpReq = (HttpServletRequest) req;
        String token = HttpReq.getHeader("Token");
        boolean isReqWithToken = null != token;

        // 请求携带 token,说明用户是在请求其他的接口,则执行 executeLogin 方法,检查 token 是否正确
        if (isReqWithToken) {
            try {
                executeLogin(req, res);
                return true;
            } catch (Exception e) {
                responseError(res, e.getMessage());
            }
        }

        // 请求不携带 token,则可能是执行登陆操作或者是游客状态访问，无需检查 token，直接返回 true
        return true;
    }

    /**
     * 执行登陆操作
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response)
            throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("Token");
        JWTToken jwtToken = new JWTToken(token);
        // 提交给realm进行登入，如果错误他会抛出异常并被捕获
        getSubject(request, response).login(jwtToken);
        // 如果没有抛出异常则代表登入成功，返回true
        return true;
    }

    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest req, ServletResponse res)
            throws Exception {
        HttpServletRequest httpReq = (HttpServletRequest) req;
        HttpServletResponse httpRes = (HttpServletResponse) res;
        httpRes.setHeader("Access-control-Allow-Origin",
                httpReq.getHeader("Origin"));
        httpRes.setHeader("Access-Control-Allow-Methods",
                "GET,POST,OPTIONS,PUT,DELETE");
        httpRes.setHeader("Access-Control-Allow-Headers",
                httpReq.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个option请求，这里我们给option请求直接返回正常状态
        if (httpReq.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpRes.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(req, res);
    }

    /**
     * 将非法请求跳转到 /unauthorized/**
     */
    private void responseError(ServletResponse res, String message) {
        try {
            HttpServletResponse httpServletResponse = (HttpServletResponse) res;
            //设置编码，否则中文字符在重定向时会变为空字符串
            message = URLEncoder.encode(message, "UTF-8");
            httpServletResponse.sendRedirect("/unauthorized/" + message);
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }
}
